from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import List, Dict, Optional
import random
from app import models, schemas

class QuestionService:
    def __init__(self, db: Session):
        self.db = db
    
    def generate_test_paper(self, config: schemas.TestConfig) -> models.TestPaper:
        """根据配置生成测试试卷"""
        # 构建查询
        query = self.db.query(models.Question)
        
        # 过滤年级
        query = query.filter(models.Question.grade_id == config.grade_id)
        
        # 过滤知识点
        if config.knowledge_point_ids:
            query = query.filter(models.Question.knowledge_point_id.in_(config.knowledge_point_ids))
        
        # 过滤难度
        if config.difficulty:
            query = query.filter(models.Question.difficulty <= int(config.difficulty))
        
        # 过滤题型
        if config.question_types:
            query = query.filter(func.upper(models.Question.type).in_([t.upper() for t in config.question_types]))
        
        # 获取所有符合条件的题目
        all_questions = query.all()
        
        if not all_questions:
            # 放宽条件的兜底策略
            relax_query = self.db.query(models.Question).filter(models.Question.grade_id == config.grade_id)
            if config.knowledge_point_ids:
                relax_query = relax_query.filter(models.Question.knowledge_point_id.in_(config.knowledge_point_ids))
            all_questions = relax_query.all()
        
        # 去重（按题干内容）
        seen = set()
        deduped = []
        for q in all_questions:
            key = (q.type, q.content.strip())
            if key not in seen:
                seen.add(key)
                deduped.append(q)
        
        # 按题型顺序选择：choice -> fill -> solve
        order = ["choice", "fill", "solve"]
        buckets = {t: [] for t in order}
        for q in deduped:
            qt = str(q.type).lower()
            if qt in buckets:
                buckets[qt].append(q)
        
        selected_questions = []
        for t in order:
            if len(selected_questions) >= config.question_count:
                break
            pool = buckets[t]
            if not pool:
                continue
            need = min(config.question_count - len(selected_questions), len(pool))
            selected_questions.extend(random.sample(pool, need))
        
        # 若还不够，补齐其他类型
        if len(selected_questions) < config.question_count:
            remaining_pool = [q for q in deduped if q not in selected_questions]
            need = min(config.question_count - len(selected_questions), len(remaining_pool))
            selected_questions.extend(random.sample(remaining_pool, need))
        
        # 最终按题型顺序排序
        def norm_type(q):
            try:
                v = q.type.value
            except Exception:
                v = str(q.type)
            return str(v).lower()
        type_rank = {"choice": 0, "fill": 1, "solve": 2}
        selected_questions.sort(key=lambda q: type_rank.get(norm_type(q), 99))
        
        # 计算总分
        total_score = sum(q.score for q in selected_questions)
        
        # 创建试卷
        test_paper = models.TestPaper(
            title=f"数学测试 - 年级{config.grade_id}",
            description=f"自动生成的数学测试试卷",
            time_limit=config.time_limit,
            total_score=total_score
        )
        
        self.db.add(test_paper)
        self.db.flush()  # 获取ID但不提交
        
        # 添加题目到试卷
        for order, question in enumerate(selected_questions, 1):
            test_paper_question = models.TestPaperQuestion(
                test_paper_id=test_paper.id,
                question_id=question.id,
                question_order=order
            )
            self.db.add(test_paper_question)
        
        self.db.commit()
        self.db.refresh(test_paper)
        
        questions = (self.db.query(models.Question)
            .join(models.TestPaperQuestion)
            .filter(models.TestPaperQuestion.test_paper_id == test_paper.id)
            .order_by(models.TestPaperQuestion.question_order)
            .all())
        
        question_schemas = [
            schemas.Question(
                id=q.id,
                content=q.content,
                type=q.type,
                options=q.options,
                correct_answer=q.correct_answer,
                explanation=q.explanation,
                difficulty=q.difficulty,
                score=q.score,
                grade_id=q.grade_id,
                knowledge_point_id=q.knowledge_point_id,
                created_at=q.created_at.isoformat() if q.created_at else None,
                updated_at=q.updated_at.isoformat() if getattr(q, 'updated_at', None) else None,
            ) for q in questions
        ]
        
        return schemas.TestPaper(
            id=test_paper.id,
            title=test_paper.title,
            description=test_paper.description,
            time_limit=test_paper.time_limit,
            total_score=test_paper.total_score,
            created_at=test_paper.created_at.isoformat() if test_paper.created_at else None,
            questions=question_schemas,
        )
    
    def submit_test(self, test_paper_id: int, answers: schemas.TestAnswers) -> schemas.TestResult:
        """提交测试答案并计算成绩"""
        # 获取试卷
        test_paper = self.db.query(models.TestPaper).filter(models.TestPaper.id == test_paper_id).first()
        if not test_paper:
            raise ValueError("Test paper not found")
        
        # 获取试卷的所有题目
        questions = (self.db.query(models.Question)
            .join(models.TestPaperQuestion)
            .filter(models.TestPaperQuestion.test_paper_id == test_paper_id)
            .order_by(models.TestPaperQuestion.question_order)
            .all())
        
        # 计算成绩
        user_score = 0
        correct_count = 0
        question_results = []
        
        for question in questions:
            user_answer = answers.answers.get(question.id) or answers.answers.get(str(question.id))
            is_correct = False
            
            if user_answer is not None:
                ua = str(user_answer).strip()
                ca = (question.correct_answer or "").strip()
                if question.type == "choice":
                    if len(ca) > 1 and ca[1] == '.':
                        ca_letter = ca[0]
                    else:
                        ca_letter = ca.upper()
                    if ua.upper() == ca_letter:
                        user_score += question.score
                        correct_count += 1
                        is_correct = True
                else:
                    if ua == ca:
                        user_score += question.score
                        correct_count += 1
                        is_correct = True
            
            # 创建带答案的问题结果
            question_result = schemas.QuestionWithAnswer(
                id=question.id,
                content=question.content,
                type=question.type,
                options=question.options,
                correct_answer=question.correct_answer,
                explanation=question.explanation,
                difficulty=question.difficulty,
                score=question.score,
                grade_id=question.grade_id,
                knowledge_point_id=question.knowledge_point_id,
                created_at=question.created_at.isoformat() if question.created_at else None,
                user_answer=user_answer,
                is_correct=is_correct
            )
            question_results.append(question_result)
        
        # 生成建议
        suggestions = self._generate_suggestions(correct_count, len(questions), question_results)
        
        # 创建测试记录（仅在用户存在时）
        try:
            user = self.db.query(models.User).filter(models.User.id == answers.user_id).first() if getattr(answers, 'user_id', None) else None
            if user:
                from datetime import datetime
                test_attempt = models.TestAttempt(
                    user_id=user.id,
                    test_paper_id=test_paper_id,
                    answers=answers.answers,
                    score=user_score,
                    status="completed",
                    correct_count=correct_count,
                    total_questions=len(questions),
                    suggestions=suggestions,
                    submit_time=datetime.utcnow()
                )
                self.db.add(test_attempt)
                self.db.commit()
            else:
                self.db.rollback()
        except Exception:
            self.db.rollback()
        
        return schemas.TestResult(
            total_score=test_paper.total_score,
            user_score=user_score,
            correct_count=correct_count,
            total_questions=len(questions),
            questions=question_results,
            suggestions=suggestions
        )
    
    def _generate_suggestions(self, correct_count: int, total_questions: int, 
                            questions: List[schemas.QuestionWithAnswer]) -> List[str]:
        suggestions = []
        accuracy = correct_count / total_questions if total_questions > 0 else 0

        # 整体表现
        if accuracy >= 0.85:
            suggestions.append("总体表现优秀，建议保持节奏并尝试更高难度练习巩固。")
        elif accuracy >= 0.6:
            suggestions.append("总体表现良好，建议针对错题进行查漏补缺，提升稳定性。")
        else:
            suggestions.append("总体正确率较低，建议系统复习本次涉及的知识点并分块训练。")

        # 知识点维度分析
        kp_stats = {}
        for q in questions:
            kp_id = q.knowledge_point_id
            if kp_id not in kp_stats:
                kp_stats[kp_id] = {"total": 0, "correct": 0}
            kp_stats[kp_id]["total"] += 1
            if q.is_correct:
                kp_stats[kp_id]["correct"] += 1
        
        # 拉取知识点名称映射
        kp_ids = list(kp_stats.keys())
        if kp_ids:
            rows = (self.db.query(models.KnowledgePoint.id, models.KnowledgePoint.name)
                .filter(models.KnowledgePoint.id.in_(kp_ids)).all())
            kp_names = {rid: name for rid, name in rows}
        else:
            kp_names = {}
        
        weak = []
        medium = []
        strong = []
        for kid, st in kp_stats.items():
            acc = st["correct"] / st["total"] if st["total"] else 0
            name = kp_names.get(kid, f"知识点#{kid}")
            if acc < 0.5:
                weak.append((name, acc))
            elif acc < 0.8:
                medium.append((name, acc))
            else:
                strong.append((name, acc))
        
        if weak:
            names = ", ".join(n for n,_ in sorted(weak, key=lambda x: x[1]))
            suggestions.append(f"薄弱知识点：{names}。建议回看课本要点，按例题—习题—错题三步强化。")
        if medium:
            names = ", ".join(n for n,_ in sorted(medium, key=lambda x: x[1]))
            suggestions.append(f"需巩固知识点：{names}。建议每日小练巩固基本题型，并适量提升难度。")
        if strong:
            names = ", ".join(n for n,_ in sorted(strong, key=lambda x: -x[1]))
            suggestions.append(f"优势知识点：{names}。建议尝试综合题与变式题，拓展思路。")

        # 难度维度提示
        hard_wrong = [q for q in questions if not q.is_correct and q.difficulty >= 4]
        if hard_wrong:
            suggestions.append("高难度题目失分较多，建议先回刷中等难度题，逐步过渡到高难度。")

        return suggestions